/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package algos;

import model.Algorithm;

/**
 *
 * @author Administrator
 */
public class NumIntModel extends Algorithm {

    private static final String algoName = "Numerical Integration";

    private int noAssetSteps = 100;

    private double[] oldValue = new double[noAssetSteps];
    private double[] newValue = new double[noAssetSteps];
    private double[] delta = new double[noAssetSteps];
    private double[] gamma = new double[noAssetSteps];
    private double[] asset = new double[noAssetSteps];

    public static final OptionType[] optionTypes = new OptionType[]{OptionType.EUROPEAN_CALL, OptionType.EUROPEAN_PUT, OptionType.AMERICAN_CALL, OptionType.AMERICAN_PUT};

    public NumIntModel() {
        super(algoName, optionTypes, new double[]{0.0,0.0});
    }

    public NumIntModel(String companyTicker, double currentStockPrice, double strikePrice, double term, double volatility, double riskFreeRate, double[] extraParametersInput) {
        super(algoName, companyTicker, currentStockPrice, strikePrice, term, volatility, riskFreeRate, optionTypes, extraParametersInput);
    }
    
    @Override
    public void initExtraParameters(double[] extraParametersInput){
    }

    public double calculatePutPrice() {

        double assetstep = 2 * super.getStrikePrice() / noAssetSteps;
        int nearestGridPoint = (int) (super.getCurrentStockPrice() / assetstep);
        double dummy = (super.getCurrentStockPrice() - nearestGridPoint * assetstep) / assetstep;
        double timestep = assetstep * assetstep / (super.getVolatility() * 4 * super.getStrikePrice() * super.getStrikePrice());
        int noTimeSteps = (int) (super.getTerm() / timestep) + 1;
        timestep = super.getTerm() / noTimeSteps;

        for (int i = 0; i < noAssetSteps; i++) {
            asset[i] = i * assetstep;
            oldValue[i] = Math.max(super.getStrikePrice() - asset[i], 0);
        }

        for (int j = 1; j < noTimeSteps; j++) {
            for (int i = 1; i < noAssetSteps - 1; i++) {
                delta[i] = (oldValue[i + 1] - oldValue[i - 1]) / (2 * assetstep);
                gamma[i] = (oldValue[i + 1] - 2 * oldValue[i] + oldValue[i - 1]) / (assetstep * assetstep);
                newValue[i] = oldValue[i] + timestep * (0.5 * super.getVolatility() * asset[i] * asset[i] * gamma[i] + super.getRiskFreeRate() * asset[i] * delta[i] - super.getRiskFreeRate() * oldValue[i]);
            }

            newValue[0] = 0;
            newValue[noAssetSteps - 1] = 2 * newValue[noAssetSteps - 2] - newValue[noAssetSteps - 3];

            for (int i = 0; i < noAssetSteps; i++) {
                oldValue[i] = newValue[i];
            }
        }

        double value = (1 - dummy) * oldValue[nearestGridPoint] + dummy * oldValue[nearestGridPoint + 1];
        return value;

    }

    public double calculateCallPrice() {

        double assetstep = 2 * super.getStrikePrice() / noAssetSteps;
        int nearestGridPoint = (int) (super.getCurrentStockPrice() / assetstep);
        double dummy = (super.getCurrentStockPrice() - nearestGridPoint * assetstep) / assetstep;
        double timestep = assetstep * assetstep / (super.getVolatility() * 4 * super.getStrikePrice() * super.getStrikePrice());
        int noTimeSteps = (int) (super.getTerm() / timestep) + 1;
        timestep = super.getTerm() / noTimeSteps;

        for (int i = 0; i < noAssetSteps; i++) {
            asset[i] = i * assetstep;
            oldValue[i] = Math.max(asset[i] - super.getStrikePrice(), 0);
        }

        for (int j = 1; j < noTimeSteps; j++) {
            for (int i = 1; i < noAssetSteps - 1; i++) {
                delta[i] = (oldValue[i + 1] - oldValue[i - 1]) / (2 * assetstep);
                gamma[i] = (oldValue[i + 1] - 2 * oldValue[i] + oldValue[i - 1]) / (assetstep * assetstep);
                newValue[i] = oldValue[i] + timestep * (0.5 * super.getVolatility() * asset[i] * asset[i] * gamma[i] + super.getRiskFreeRate() * asset[i] * delta[i] - super.getRiskFreeRate() * oldValue[i]);
            }

            newValue[0] = 0;
            newValue[noAssetSteps - 1] = 2 * newValue[noAssetSteps - 2] - newValue[noAssetSteps - 3];

            for (int i = 0; i < noAssetSteps; i++) {
                oldValue[i] = newValue[i];
            }
        }

        double value = (1 - dummy) * oldValue[nearestGridPoint] + dummy * oldValue[nearestGridPoint + 1];
        return value;
    }

    public static void main(String[] args) {
        Algorithm a = new NumIntModel("A", 50, 40, 0.4, 0.4, 0.1, new double[]{});
        System.out.println(a.calculateCallPrice());
        System.out.println(a.calculatePutPrice());
    }

}
